在一個完整的訂單系統中,「付款」是最關鍵的一步。
過去幾天我們實作了「下單 → 通知 → 狀態更新」,但顧客仍需手動付款或到現場結帳。
今天,我們要踏出整合金流 API 的第一步,讓系統能自動建立付款連結,完成線上支付。
金流串接讓你的系統具備三大能力:
顧客端自助付款:顧客可在線上直接付款,不再需要人工確認。
後台即時回報:付款成功後系統自動更新訂單狀態。
降低錯誤與時間成本:不需對帳、對金額、對收款人。
目前台灣常見金流平台有:
綠界(ECPay):老牌穩定,API 完整。
藍新(NewebPay):支援多種付款方式。
LINE Pay:與 LINE OA 結合方便,但開發門檻較高。
今天我們會用 綠界金流 Sandbox 進行示範(免費測試環境)。
讓我們先看一下整體流程,了解系統中資料的流向:

這是一個「三段式」流程:
由系統後端建立交易。
顧客前端開啟付款頁面。
金流服務回呼通知結果。
.env 金流設定首先在專案根目錄的 .env 新增 Sandbox 參數(以綠界為例):
ECPAY_MERCHANT_ID=2000132
ECPAY_HASH_KEY=5294y06JbISpM5x9
ECPAY_HASH_IV=v77hoKGq4kWxNNIS
ECPAY_RETURN_URL=https://yourdomain.com/payment/webhook
ECPAY_CLIENT_BACK_URL=https://yourdomain.com/liff-form/success
ECPAY_API_URL=https://payment-stage.ecpay.com.tw/Cashier/AioCheckOut/V5
Sandbox 的 MerchantID 固定是
2000132,HashKey 與 HashIV 為官方提供的測試值。
新增路由 routes/payment.js:
const express = require("express");
const router = express.Router();
const crypto = require("crypto");
const querystring = require("querystring");
router.post("/create", async (req, res) => {
const { orderId, amount, itemName } = req.body;
const tradeNo = `T${Date.now()}`; // 測試用交易編號
const params = {
MerchantID: process.env.ECPAY_MERCHANT_ID,
MerchantTradeNo: tradeNo,
MerchantTradeDate: new Date().toLocaleString("zh-TW", { hour12: false }),
PaymentType: "aio",
TotalAmount: amount,
TradeDesc: "LINE 訂單付款",
ItemName: itemName,
ReturnURL: process.env.ECPAY_RETURN_URL,
ClientBackURL: process.env.ECPAY_CLIENT_BACK_URL,
ChoosePayment: "ALL",
};
// 組出 CheckMacValue (雜湊驗證)
const raw = `HashKey=${process.env.ECPAY_HASH_KEY}&${querystring.stringify(params)}&HashIV=${process.env.ECPAY_HASH_IV}`;
const encoded = encodeURIComponent(raw).toLowerCase().replace(/%20/g, '+');
const checkMacValue = crypto.createHash("sha256").update(encoded).digest("hex").toUpperCase();
params.CheckMacValue = checkMacValue;
const form = `
<form id="ecpay-form" method="POST" action="${process.env.ECPAY_API_URL}">
${Object.entries(params).map(([k, v]) => `<input type="hidden" name="${k}" value="${v}" />`).join("")}
</form>
<script>document.getElementById("ecpay-form").submit();</script>
`;
res.send(form);
});
module.exports = router;
在 index.js 引入:
const paymentRoutes = require('./src/routes/payment');
app.use('/payment', paymentRoutes);
這樣前端就能呼叫 /payment/create 拿到自動送出的付款表單。
在你的 LIFF 或管理頁中,按下「付款」按鈕後:
fetch("/payment/create", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
orderId: "664a21b...",
amount: 120,
itemName: "紅茶拿鐵 x2",
}),
})
.then((res) => res.text())
.then((html) => {
const blob = new Blob([html], { type: "text/html" });
const url = URL.createObjectURL(blob);
window.location.href = url;
});
使用者會被導向綠界 Sandbox 的付款頁面,進行測試交易。
付款後綠界會發送 POST webhook 到你的 /payment/webhook,
裡面會包含交易結果、金額、驗證碼等資訊。
範例 payload:
{
"MerchantID": "2000132",
"RtnCode": "1",
"RtnMsg": "交易成功",
"MerchantTradeNo": "T1728658000000",
"TradeAmt": "120"
}
下一篇(Day 28)我們將處理這段 webhook,讓系統能自動:
「付款成功 → 訂單狀態改為 Completed → 同時通知顧客與群組」
認識金流串接流程(建立交易 → 導向付款 → 接 webhook)。
建立第一個可運作的綠界 Sandbox 付款 API。
理解 CheckMacValue 驗證與金流安全性的重要性。
為明日的 webhook 實作打好基礎。
明天我們將實作「金流 webhook 整合」,
讓整個流程自動閉環:
付款 → 更新狀態 → Redis Worker 通知 → LINE 推播 💬
真正達成「全自動化訂單系統」的最後拼圖!